#include<cstdio>
#include<cstring>
#include<iostream>
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
#include<stdlib.h>

#define FORMAT "[%s@%s %s]& "
#define COMMAND_SIZE 1024

#define SIZE 128
char* g_argv[SIZE];
int g_argc = 0;
int codelast = 0;

char* g_env[1000];
int g_envs = 0;

char cwd[1024];
char cwdenv[1024];
const char* GetUserName()
{
  const char* name = getenv("USER");
  return name == NULL ? "None":name;
}



const char* GetHostName()
{
  const char* name = getenv("HOSTNAME");
  return name == NULL ? "None" : name;
}




const char* GetPwd()
{
 // const char* pwd = getenv("PWD");
  char* pwd = getcwd(cwd,sizeof(cwd));
  if(pwd != NULL)
  {
    snprintf(cwdenv,sizeof(cwdenv),"PWD=%s",pwd);
    putenv(cwdenv);
  }
  return pwd == NULL ? "None": pwd;
}

const char* GetHome()
{
  const char* home = getenv("HOME");
  return home == NULL ? "None" : home;
}




std::string DirName(const char* Pwd)
{
#define SLASH "/"
  std::string dir = Pwd;
  if(dir == SLASH) return SLASH;
  size_t pos = dir.rfind(SLASH);
  if(pos == std::string::npos) return "BUG?";
  return dir.substr(pos+1);
}





void MakeCommandLine(char* prompt,int size)
{
  snprintf(prompt,size,FORMAT,GetUserName(),GetHostName(),DirName(GetPwd()).c_str());
}



void PrintfCommandPrompt()
{
  char prompt[COMMAND_SIZE];
  MakeCommandLine(prompt,sizeof(prompt));
  printf("%s",prompt);
  fflush(stdout);
}


bool GetCommandLine(char* prompt,int size)
{
  char* tmp = fgets(prompt,size,stdin);
  if(tmp == NULL) return false;
  prompt[strlen(prompt) - 1] = 0;
  if(strlen(prompt) == 0) return false;
  return true;

}


bool CommandParse(char* prompt)
{
#define SEP " "
  g_argc = 0;
  g_argv[g_argc++] = strtok(prompt,SEP);
  while((bool)(g_argv[g_argc++] = strtok(nullptr,SEP)));
  g_argc--;
  return g_argc>0 ? true : false;
}


int Execute()
{
  pid_t id = fork();
  if(id == 0)
  {
    execvp(g_argv[0],g_argv);
    exit(1);
  }
  int status = 0;
  pid_t rid = waitpid(id,&status,0);
  if(rid > 0)
  {
    codelast = WEXITSTATUS(status);
  }
  return 0;
}

bool CD()
{
  if(g_argc == 1)
  {
    chdir(GetHome());
  }
  else 
  {
    std::string tmp = g_argv[1];
    chdir(tmp.c_str());
  }
  return true;
}


bool ECHO()
{
  if(g_argc == 2)
  {
    std::string where = g_argv[1];
    if(strcmp(where.c_str(),"$?") == 0)
    {
      std::cout<< codelast<<std::endl;
      codelast = 0;
    }
    else if(where[0] == '$')
    {
      std::string env_name = where.substr(1);
      const char* env_value = getenv(env_name.c_str());
      if(env_value)
      std::cout<<env_value<<std::endl;

    }
    else 
    {
      std::cout<<where<<std::endl;
    }
    return true;
  }
  return true;
}



bool CheckAndExecBuiltin()
{
  std::string where = g_argv[0];
  if(where == "cd")
  {
    CD();
    return true;
  }
  else if(where == "echo")
  {
    ECHO();
    return true;
  }
  return false;
}


void InitEnv()
{
  extern char** environ;
  memset(g_env,0,sizeof(g_env));

  for(int i = 0; environ[i]; i++)
  {
    printf("environ_i=%d\n", i);
    g_env[i] = (char*)malloc(strlen(environ[i])+1);
    strcpy(g_env[i],environ[i]);
    g_envs++;
  }
  g_env[g_envs] = NULL;
  printf("g_envs=%d\n", g_envs);

  for(int i = 0; g_env[i]; i++)
  {
    printf("i=%d\n", i);
    putenv(g_env[i]);
  }
  //不要加,putenv让所有的environ都发生写实拷贝了。
 // environ = g_env;
}


void clear()
{
  for(int i = 0 ; g_env[i]; i++)
  {
    printf("i_clear=%d\n", i);
     free(g_env[i]);
  }
}



int main()
{
  InitEnv();
  while(true)
  {
    PrintfCommandPrompt();
    char prompt[COMMAND_SIZE];
    if(!(GetCommandLine(prompt,sizeof(prompt))))
      continue;
    if(!(CommandParse(prompt)))
      continue;
    if(CheckAndExecBuiltin())
      continue;
    if(strcmp(g_argv[0],"1") == 0)
    {
      printf("退出了!\n");
      break;
    }
    Execute();
  }
  clear();
  return 0;
}
